/* 
 * File:   main.c
 * Author: chw412
 *
 * Created on September 13, 2012, 11:13 AM
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define LINES_COUNT 0x1		//  -l
#define WORDS_COUNT 0x2 	//  -w
#define BYTES_COUNT 0x4		//  -c
#define CHARACTERS_COUNT 0x8	//  -Cm
#define LINES 0
#define WORDS 1
#define BYTES 2
#define CHARACTERS 3
/*
 * 
 */
extern char *optarg;   //the pointer to the argument of opti
extern int optind;
extern int optopt;
extern int opterr;

int op_code = 0x0;
long long int count[4];  //the array used to store the counts of lines, words, bytes and characters
long long int all_count[4];

void usage()
{
	fprintf(stderr, "usage: wc [-c | -m | -C] [-lw] [file ...]\n");
}

int isdelimiter(char ch)
{
	return iswspace(ch);
}

#define COUNT_LINES_BYTES_CHARACTERS	do	 \
{						 \
	if(ch == '\n')				 \
		count[LINES]++;			 \
	count[BYTES]++;				 \
	count[CHARACTERS]++;			 \
}while(0)

void count_lines_words_bytes_characters(FILE * fp)
{
	char ch;

	for(ch = fgetc(fp); !feof(fp); ch = fgetc(fp))
	{
		//count words when we met with a word's two delimiters
		if(isdelimiter(ch))
		{
			for(; !feof(fp) && isdelimiter(ch); ch = fgetc(fp))
			{
				COUNT_LINES_BYTES_CHARACTERS;
			}
			if(!feof(fp))
				COUNT_LINES_BYTES_CHARACTERS;
		}
		else
		{
			count[WORDS]++;
			for(; !feof(fp) && !isdelimiter(ch); ch = fgetc(fp))
			{
				COUNT_LINES_BYTES_CHARACTERS;
			}
			if(!feof(fp))
				COUNT_LINES_BYTES_CHARACTERS;
		}

	}
}

void connect_strings(char * buffer, const char * tmp)
{
	int i = 0;
	int start = strlen(buffer);
	for(i = 0; i < strlen(tmp); ++i)
		*(buffer + start + i) = tmp[i];
	*(buffer + start + strlen(tmp)) = '\0';
}

void formatting(char * buffer, char * tmp, int op_code, char * str, long long int * ct)
{
		if(op_code & LINES_COUNT)
		{
			sprintf(tmp, "%8lld", ct[LINES]);
			connect_strings(buffer, tmp);
		}
		if(op_code & WORDS_COUNT)
		{
			sprintf(tmp, "%8lld", ct[WORDS]);
			connect_strings(buffer, tmp);
		}

		//note that there will not any chance for the program to go into 'both' the following two "if"s
		//since any combination of both these two options: ie. -Cc -cm have been deminated previouly
		if(op_code & BYTES_COUNT)
		{
			sprintf(tmp, "%8lld", ct[BYTES]);
			connect_strings(buffer, tmp);
		}
		if(op_code & CHARACTERS_COUNT)
		{
			sprintf(tmp, "%8lld", ct[CHARACTERS]);
			connect_strings(buffer, tmp);
		}

		sprintf(tmp, " %s", str);
		connect_strings(buffer, tmp);

}
int main(int argc, char** argv) {

    optind = 1;
    
    int not_default_option = 0;
    int result;
   
    while ((result = getopt(argc, argv, "cCmlw")) != -1)
    {
	    
    	switch((char)result)
	{
		case 'l':
			not_default_option = 1;
			op_code |= LINES_COUNT;
			break;
		case 'w':
			not_default_option = 1;
			op_code |= WORDS_COUNT;
			break;
		case 'c':
			not_default_option = 1;
			op_code |= BYTES_COUNT;
			break;
		case 'C': case 'm':
			not_default_option = 1;
			op_code |= CHARACTERS_COUNT;
			break;
		default:
			//the option is illegal, so print out the usage.
			usage();
			return 1;			
	}
    }

    //when the combinations of options of (-c & -C) or (-c & -m) are designated, report the usage and exit
    if((op_code & BYTES_COUNT) != 0 && (op_code & CHARACTERS_COUNT) != 0)
    {    
	usage();
    	return 1;
    }

    //by default, the operations would be -lwc
    if(!not_default_option)
    	op_code = LINES_COUNT | WORDS_COUNT | BYTES_COUNT;
    
    int file_count = 0, i = 0;
    FILE * fp = NULL;
    char buffer[1024] = {0};
    char tmp[64] = {0};

    all_count[LINES] = 0;
    all_count[WORDS] = 0;
    all_count[BYTES] = 0;
    all_count[CHARACTERS] = 0;

    if(optind == argc)
	    //the program has no designated files as input, so let's start this program with standard input
    {
    	count[LINES] = 0;
	count[WORDS] = 0;
	count[BYTES] = 0;
	count[CHARACTERS] = 0;

	count_lines_words_bytes_characters(stdin);
	memset(buffer, 0, 1024);
	formatting(buffer, tmp, op_code, " ", count);	
	fprintf(stdout, "%s\n", buffer);
	return (EXIT_SUCCESS);
    }

    for(i = optind; i < argc; i++)
    {
	count[LINES] = 0;
	count[WORDS] = 0;
	count[BYTES] = 0;
	count[CHARACTERS] = 0;

    	if((fp = fopen(argv[i], "r")) == NULL)
		fprintf(stderr, "wc: cannot open %s\n", argv[i]);
	else
	{
		count_lines_words_bytes_characters(fp);
		memset(buffer, 0, 1024);
		formatting(buffer, tmp, op_code, argv[i], count);	
		fprintf(stdout, "%s\n", buffer);

		all_count[LINES] += count[LINES];
		all_count[WORDS] += count[WORDS];
		all_count[BYTES] += count[BYTES];
		all_count[CHARACTERS] += count[CHARACTERS];

		fclose(fp);
	}
    }

    if(argc - optind >= 2) // the program has two or more files as its parameters, so we need to calculate the total
    {	
	memset(buffer, 0, 1024);
	formatting(buffer, tmp, op_code, "total", all_count);	
    	fprintf(stdout, "%s\n", buffer);
    }

    return (EXIT_SUCCESS);
}

